package org.yunai.swjg.server.module.activity;

import org.slf4j.Logger;
import org.yunai.swjg.server.core.constants.ActivityConstants;
import org.yunai.swjg.server.core.constants.SysMessageConstants;
import org.yunai.swjg.server.core.service.GameMessageProcessor;
import org.yunai.swjg.server.core.service.Online;
import org.yunai.swjg.server.core.service.OnlineContextService;
import org.yunai.swjg.server.core.thread.CrossThreadOperationService;
import org.yunai.swjg.server.module.activity.heatbeat.ActivityBossReviveTask;
import org.yunai.swjg.server.module.activity.msg.SysActivityChangeStatusMessage;
import org.yunai.swjg.server.module.activity.operation.ActivityBossGivePrizeOperation;
import org.yunai.swjg.server.module.battle.command.BattleService;
import org.yunai.swjg.server.module.currency.Currency;
import org.yunai.swjg.server.module.currency.CurrencyService;
import org.yunai.swjg.server.module.monster.vo.VisibleMonster;
import org.yunai.swjg.server.module.player.vo.Player;
import org.yunai.swjg.server.module.rep.ActivityRepScene;
import org.yunai.swjg.server.module.rep.RepSceneService;
import org.yunai.swjg.server.module.rep.callback.PlayerEnterRepCallback;
import org.yunai.swjg.server.module.rep.template.RepTemplate;
import org.yunai.swjg.server.rpc.message.S_C.*;
import org.yunai.swjg.server.rpc.struct.StActivityBossPlayerRank;
import org.yunai.yfserver.common.LoggerFactory;
import org.yunai.yfserver.message.sys.SysInternalMessage;
import org.yunai.yfserver.spring.BeanManager;
import org.yunai.yfserver.time.TimeService;
import org.yunai.yfserver.util.MathUtils;
import org.yunai.yfserver.util.TimeUtils;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Boss活动
 * User: yunai
 * Date: 13-5-20
 * Time: 下午3:25
 */
public class BossActivity extends AbstractActivity {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerFactory.Logger.activity, BossActivity.class);

    //    private static NormalSceneService normalSceneService;
    private static TimeService timeService;
    private static RepSceneService repSceneService;
    private static CurrencyService currencyService;
    private static CrossThreadOperationService crossThreadOperationService;
    private static BattleService battleService;
    private static OnlineContextService onlineContextService;
    private static GameMessageProcessor gameMessageProcessor;
    static {
//        normalSceneService = BeanManager.getBean(NormalSceneService.class);
        timeService = BeanManager.getBean(TimeService.class);
        repSceneService = BeanManager.getBean(RepSceneService.class);
        currencyService = BeanManager.getBean(CurrencyService.class);
        crossThreadOperationService = BeanManager.getBean(CrossThreadOperationService.class);
        battleService = BeanManager.getBean(BattleService.class);
        onlineContextService = BeanManager.getBean(OnlineContextService.class);
        gameMessageProcessor = BeanManager.getBean(GameMessageProcessor.class);
    }

    /**
     * 排行版人数
     */
    private static final int RANK_SIZE = 10;

    // TODO BOSS如果在15分钟内被玩家击杀，会提升一级
    /**
     * 玩家输出<br />
     * KEY: 玩家编号<br />
     * VALUE: 伤害
     */
    private Map<Integer, Integer> playerDamages;
    /**
     * 玩家死亡时间Map<br />
     * KEY: 玩家编号<br />
     * VALUE: 复活时间
     */
    private Map<Integer, Long> playerReviveTimes;
    /**
     * 玩家鼓舞次数Map<br />
     * KEY: 玩家编号<br />
     * VALUE: 玩家鼓舞次数
     */
    private Map<Integer, Integer> playerEncourages;
    private List<StActivityBossPlayerRank> playerDamageRank;

    private final RepTemplate repTemplate;
    /**
     * BOSS活动对应副本
     */
    private volatile ActivityRepScene rep;
    /**
     * 进入[准备阶段]时间
     */
    private long prepareTime;
    /**
     * 进入[进行阶段]时间
     */
    private long processTime;
    /**
     * 进入[结束阶段]时间
     */
    private long endTime;

    public BossActivity(Integer actId, int sceneId) {
        super(actId);
        repTemplate = RepTemplate.getBySceneId(sceneId);
    }

    /**
     * 奖励：
     * 战斗之后给：
     * 1、每次战斗造成10点伤害奖励1铜钱，向上整取。铜钱奖励上限 = 等级*100
     * 每次战斗造成10000点伤害奖励1声望，向上整上取。声望奖励上限为30声望
     * 3、最后一位对BOSS造成伤害的玩家获得：（BOSS等级-20）*100000的铜钱奖励
     * ----------------
     * 结算时：
     * 2、对BOSS造成伤害的前三名玩家，有额外奖励
     * 第一名 丹药制作卷随机一件、紫色装备制作卷随机一件，BOSS变身卡1张
     * 第二名 丹药制作卷、蓝色装备随机一件
     * 第三名 蓝色装备随机一件
     * 4、BOSS战结束后每位对世界BOSS伤害超过1%的玩家可获得10000铜钱
     * 5、在击杀boss时，游戏中会显示击杀前十的玩家角色排名信息，世界boss前十声望奖励可是非常的多哦 200声望
     * ps: 结算
     * ----------------
     * 5、BOSS被击杀后，能使用附加功能快速复活。
     */

    @Override
    public void join(Online online) {
        if (getStatus() == ActivityDef.Status.END) {
            online.write(new S_C_SysMessageReq(SysMessageConstants.ACTIVITY_BOSS_END));
            return;
        }
        repSceneService.onPlayerLeaveScene(online, new PlayerEnterRepCallback(online.getPlayer().getId(), repTemplate.getId()));
    }

    /**
     * TODO
     *
     * @param online 在线信息
     */
    public void afterJoin(Online online) {
        // BOSS活动信息
        long now = timeService.now();
        int processRemainTime = getStatus() == ActivityDef.Status.PREPARE ? (int) MathUtils.ceilX(processTime - now, TimeUtils.SECOND) : 0;
        int remainTime = getStatus() == ActivityDef.Status.PROCESS ? (int) MathUtils.ceilX(now - processTime, TimeUtils.SECOND) :
                getStatus() == ActivityDef.Status.PREPARE ? getActivityTemplate().getProcessStageTime() * 60 : 0;
        online.write(new S_C_ActivityBossInfoResp(getActivityTemplate().getId().byteValue(), rep.playerSize(), processRemainTime,
                remainTime, getStatus().getValue()));
        // BOSS活动玩家信息
        Integer damage = getDamage(online);
        Integer aliveTime = getAliveTime(online);
        Byte dead = aliveTime > 0 ? (byte) 1 : (byte) 0;
        Byte encourageCount = getEncourage(online).byteValue();
        online.write(new S_C_ActivityBossPlayerInfoResp(damage, aliveTime, dead, encourageCount));
        // 输出排行版
        if (getStatus() != ActivityDef.Status.PREPARE) {
            online.write(new S_C_ActivityBossRankResp(playerDamageRank));
        }
        // 输出是否能够移动过屏障(不死就可以了并且活动进行中)
        if (dead == 0 && getStatus() == ActivityDef.Status.PROCESS) {
            online.write(new S_C_ActivityBossCanMoveResp());
        }
    }

    @Override
    public void prepare() {
        if (!checkStatus(ActivityDef.Status.PREPARE)) {
            LOGGER.error("[prepare] [activity({})'s status is {}].", getActivityTemplate().getId(), getStatus());
            return;
        }
        // 开启副本
        rep = repSceneService.createActivityRep(repTemplate.getId());
        if (rep == null) {
            LOGGER.error("[prepare] [activity({})'s rep can't create].", getActivityTemplate().getId());
            return;
        }
        // 初始化时间
        prepareTime = timeService.now();
        processTime = prepareTime + super.getActivityTemplate().getPrepareStageTime() * TimeUtils.MINUTE;
        endTime = processTime + super.getActivityTemplate().getProcessStageTime() * TimeUtils.MINUTE;
        // 清理伤害
        playerDamages = new HashMap<>();
        playerReviveTimes = new HashMap<>();
        playerEncourages = new HashMap<>();
        playerDamageRank = new LinkedList<>();
        // 设置活动状态为[准备中]阶段
        super.setStatus(ActivityDef.Status.PREPARE);
        // 广播活动开始
        onlineContextService.broadcastMessage(new S_C_ActivityStartResp(super.getActivityTemplate().getId().byteValue()));
    }

    @Override
    public void process() {
        if (rep == null) {
            LOGGER.error("[process] [activity({})'s rep is null] [status is {}].", getActivityTemplate().getId(), getStatus());
            return;
        }
        rep.putMessage(new SysInternalMessage() {
            @Override
            public void execute() {
                if (!BossActivity.this.checkStatus(ActivityDef.Status.PROCESS)) {
                    LOGGER.error("[process] [activity({})'s status is {}].", getActivityTemplate().getId(), getStatus());
                    return;
                }
                // TODO 测试数据
                playerDamages.put(11, 100);
                playerDamages.put(12, 200);
                BossActivity.this.update("测试", 100);
                BossActivity.this.update("奥特曼", 200);
                // 提交复活心跳任务
                rep.addTask(new ActivityBossReviveTask(rep, playerReviveTimes));
                // 设置活动状态为[进行中]阶段
                BossActivity.this.setStatus(ActivityDef.Status.PROCESS);
                // 场景广播玩家能够移动过屏障
                rep.broadcastMessage(new S_C_ActivityBossCanMoveResp());
            }

            @Override
            public short getCode() {
                return 0;
            }
        });
    }

    @Override
    public void end() {
        if (rep == null) {
            LOGGER.error("[end] [activity({})'s rep is null] [status is {}].", getActivityTemplate().getId(), getStatus());
            return;
        }
        rep.putMessage(new SysInternalMessage() {
            @Override
            public void execute() {
                if (!BossActivity.this.checkStatus(ActivityDef.Status.END)) {
                    LOGGER.error("[end] [activity({})'s status is {}].", getActivityTemplate().getId(), getStatus());
                    return;
                }
                // 备份下伤害记录
                final Map<Integer, Integer> playerDamagesBak = new HashMap<>(playerDamages);
                // 开始清理
                playerDamages.clear();
                playerDamages = null;
                playerReviveTimes.clear();
                playerReviveTimes = null;
                playerEncourages.clear();
                playerEncourages = null;
                playerDamageRank.clear();
                playerDamageRank = null;
                rep = null;
                // 设置活动状态为[结束]阶段
                BossActivity.this.setStatus(ActivityDef.Status.END);
                // 发送消息给主消息队列进行发奖励 TODO 未完整
                // TODO 这样发奖励，如果关闭服务器时，消息队列未执行完，会导致奖励没发送！
                // TODO 目前想到的办法是，先记录日志，然后服务器关闭完的时候，检查下奖励是否发了。。
                gameMessageProcessor.put(new SysInternalMessage() {
                    @Override
                    public void execute() {
                        for (Integer playerId : playerDamagesBak.keySet()) {
                            crossThreadOperationService.execute(new ActivityBossGivePrizeOperation(playerId));
                        }
                    }

                    @Override
                    public short getCode() {
                        return 0;
                    }
                });
            }

            @Override
            public short getCode() {
                return 0;
            }
        });

    }

    public void playerBeginAttack(Online online, Integer monsterId) {
        if (getStatus() != ActivityDef.Status.PROCESS) {
            return;
        }
        // 检查是否死亡
        if (getAliveTime(online) > 0) {
            online.write(new S_C_SysMessageReq(SysMessageConstants.ACTIVITY_BOSS_PLAYER_DEAD));
            return;
        }
        // 检查是否在当前副本
        if (online.getScene() != rep) {
            online.write(new S_C_SysMessageReq(SysMessageConstants.MONSTER_NOT_FOUND));
            return;
        }
        VisibleMonster monster = rep.getMonster(monsterId);
        battleService.battleBossMonster(online, this, monster);
    }

    // boss战斗前的血，boss战斗后的血。战斗，任务怪
    public void playerEndAttack(Online online, VisibleMonster monster, int oldHp, int nowHp) {
        // 记录伤害并进行伤害排行版重排
        int lossHp = oldHp - nowHp; // TODO 是否有必要考虑血反倒多了的情况= =！
        addDamage(online, lossHp);
        online.write(new S_C_ActivityBossRankResp(playerDamageRank));
        // 广播怪物血减少
        rep.broadcastMonsterHpReduceMessage(monster, lossHp);
        // 伤害奖励
        int bonusCoin = MathUtils.ceil2Int(lossHp / 10.0);
        int maxBonusCoin = monster.getLevel() * 100;
        if (bonusCoin > maxBonusCoin) {
            bonusCoin = maxBonusCoin;
        }
        if (currencyService.canGive(online.getPlayer(), Currency.COIN, bonusCoin, true)) {
            currencyService.give(online.getPlayer(), Currency.COIN, bonusCoin);
        }
        int bonusReputation = MathUtils.ceil2Int(lossHp / 10000.0);
        if (bonusReputation > 30) {
            bonusReputation = 30;
        }
        // TODO 声望
        // 击杀
        if (nowHp == 0) {
            int killBonusCoin = (monster.getLevel() - 20) * 100000;
            if (currencyService.canGive(online.getPlayer(), Currency.COIN, killBonusCoin, true)) {
                currencyService.give(online.getPlayer(), Currency.COIN, killBonusCoin);
            }
            bonusCoin += killBonusCoin;
            // TODO boss被击杀消息广播下给玩家
        } else {
            playerReviveTimes.put(online.getPlayer().getId(), timeService.now() + ActivityConstants.BOSS_ACTIVITY_RELIVE);
            repSceneService.onDefaultRelive(online);
        }
        // TODO 消息 [战斗结果][伤害][铜钱][声望]
        // 当血为0时，设置怪物死亡并广播，并结束活动
        if (nowHp == 0 && rep.aliveMonsterCount() == 0) {
            monster.setAlive(false);
            S_C_MonsterDelResp monsterDelResp = new S_C_MonsterDelResp(monster.getId());
            rep.broadcastMessage(monsterDelResp);
            // 发送活动结束消息
            gameMessageProcessor.put(new SysActivityChangeStatusMessage(getActivityTemplate().getId(), ActivityDef.Status.END));
        }
    }

    /**
     * 增加玩家的伤害，并重新排序排行版
     *
     * @param online 在线信息
     * @param incrDamage 新增伤害
     */
    private void addDamage(Online online, Integer incrDamage) {
        Player player = online.getPlayer();
        Integer playerId = player.getId();
        Integer damage = playerDamages.get(playerId);
        damage = damage != null ? damage + incrDamage : incrDamage;
        playerDamages.put(playerId, damage);
        update(player.getNickname(), damage);
    }

    private Integer getDamage(Online online) {
        Integer playerId = online.getPlayer().getId();
        Integer damage = playerDamages.get(playerId);
        return damage != null ? damage : 0;
    }

    private Integer getAliveTime(Online online) {
        Integer playerId = online.getPlayer().getId();
        Long reliveTime = playerReviveTimes.get(playerId);
        return reliveTime != null ? (int) MathUtils.ceilX(reliveTime - timeService.now(), TimeUtils.SECOND) : 0;
    }

    private Integer getEncourage(Online online) {
        Integer playerId = online.getPlayer().getId();
        Integer encourage = playerEncourages.get(playerId);
        return encourage != null ? encourage : 0;
    }

    /**
     * 更新伤害排行版
     *
     * @param nickname  玩家昵称
     * @param newDamage 伤害
     */
    private void update(String nickname, Integer newDamage) {
        int exist = -1;
        int high = playerDamageRank.size() - 1;
        for (int i = 0; i <= high; i++) { // TODO 由于playerDamageRank是LinkedList，遍历方式稍微需要调整下
            if (playerDamageRank.get(i).getNickname().equals(nickname)) { // TODO 这里字符串比较很尴尬。
                exist = high = i;
                break;
            }
        }
        int i = exist != -1 ? (high - 1) : high;
        for (; i >= 0; i--) {
            if (playerDamageRank.get(i).getDamage() >= newDamage) {
                high = i + 1;
                break;
            }
        }
        if (i == -1) {
            high = 0;
        }
        if (high >= RANK_SIZE) {
            return;
        }
        if (high == exist) {
            playerDamageRank.get(exist).setDamage(newDamage);
            return;
        }
        if (exist == -1) {
            if (playerDamageRank.size() == RANK_SIZE) {
                playerDamageRank.remove(RANK_SIZE - 1);
            }
            playerDamageRank.add(high, new StActivityBossPlayerRank(newDamage, nickname));
        } else {
            StActivityBossPlayerRank rank = playerDamageRank.remove(exist);
            rank.setDamage(newDamage);
            playerDamageRank.add(high, rank);
        }
    }
}
